<?php

/*
 * Copyright (C) 2006-2009 Pham Cong Dinh
 *
 * This file is part of Pone.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 3 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

namespace \spica\core\datasource;

/**
 * A connection (session) with a Drizzle database
 *
 * @see        http://jluedke.com/drizzle/Drizzle.txt
 * @see        http://svn.php.net/viewvc/pecl/drizzle/trunk
 *
 * @category   spica
 * @package    core
 * @package    datasource
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.1
 * @since      November 30, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id$
 */
class DrizzleConnection implements Pone_Database_Connection
{
    /**
     * Database host.
     *
     * @var string
     */
    protected $_dbhost;

    /**
     * Database user name.
     *
     * @var string
     */
    protected $_dbuser;

    /**
     * Database name.
     *
     * @var string
     */
    protected $_db;

    /**
     * Database password associated with the above database user.
     *
     * @var string
     */
    protected $_dbpassword;

    /**
     * Database port that database server used.
     *
     * @var int default to 4427
     */
    protected $_dbport = 4427;

    /**
     * Database socket, something like "/tmp/mysql.sock".
     *
     * @var string
     */
    protected $_dbsocket;

    /**
     * MySQL connection flags.
     *
     * MYSQLI_CLIENT_COMPRESS    	Use compression protocol
     * MYSQLI_CLIENT_FOUND_ROWS 	return number of matched rows, not the number of affected rows
     * MYSQLI_CLIENT_IGNORE_SPACE 	Allow spaces after function names. Makes all function names reserved words.
     * MYSQLI_CLIENT_INTERACTIVE 	Allow interactive_timeout seconds (instead of wait_timeout seconds) of inactivity before closing the connection
     * MYSQLI_CLIENT_SSL            Use SSL (encryption)
     *
     * @see http://vn2.php.net/mysqli_real_connect
     * @var int
     */
    protected $_dbflag = null;

    /**
     * Has the connections closed.
     *
     * @var bool
     */
    protected $_isClosed = true;

    /**
     * Is the connection protected from updating, deleting, inserting or replacing data.
     *
     * @var bool
     */
    protected $_readOnly = false;

    /**
     * An object of mysqli that keeps a native database connection to a MySQL Server.
     *
     * @var DrizzleCon
     */
    protected $_connection = null;

    /**
     * Drizzle instance.
     *
     * @var Drizzle
     */
    protected $_drizzle;

    /**
     * An array of open statements that is created by createStatement().
     *
     * @var array
     */
    protected $_openStatements = array();

    /**
     * Is debug mode on?
     *
     * @var bool
     */
    protected $_debugMode = false;

    /**
     * Determine if MySQL database server should commit the action result right after
     * the update/delete/insert query executed or not.
     *
     * The default behaviour is to use transaction
     *
     * @var bool
     */
    protected $_autoCommit = false;

    /**
     * Transaction isolation level.
     *
     * @var string
     */
    protected $_isolationLevel = 'READ COMMITTED';

    /**
     * Has the transaction been started.
     *
     * @var bool
     */
    protected $_transactionStarted = false;

    /**
     * The number of currently open transactions.
     *
     * @var int
     */
    protected $_transactionCount = 0;

    /**
     * Has this connection supported multiple queries sent to server as a single one.
     *
     * @var bool
     */
    protected $_allowedMultipleQueries = true;

    /**
     * Has connection been initialized with drizzle_create()?
     *
     * @var bool
     */
    protected $_connectionInitialized = false;

    /**
     * Has connection been established with drizzle_con_add_tcp()?
     *
     * @var bool
     */
    protected $_connectionEstablished = false;

    /**
     * The largest packet we can send (changed once we know what the server supports, we get this at connection init)
     *
     * @var int
     */
    protected $_maxAllowedPacket = 65536;

    /**
     * Max re-connecting attempts.
     *
     * @var int
     */
    protected $_maxReconnects = 3;

    /**
     * Identify if mixed case unquoted SQL identifiers can be treated as lower case, upper case or mixed case
     *
     * Value can be: Pone_Database_ResultSet::IDENTIFIER_LOWERCASE
     *               Pone_Database_ResultSet::IDENTIFIER_UPPERCASE
     *               Pone_Database_ResultSet::IDENTIFIER_MIXEDCASE
     * @var int
     */
    protected $_sqlIdentifierCase = Pone_Database_ResultSet::IDENTIFIER_LOWERCASE;

    /**
     * Constructs an object of <code>SpicaDrizzleConnection</code>.
     *
     * Drizzle supports UTF8 only so there is no need for character set settings.
     *
     * @param array $params
     * @param bool $persistent
     */
    public function  __construct($params = array(), $persistent = false)
    {
        if (false === is_array($params) || empty($params))
        {
            throw new InvalidArgumentException('The first parameter to '.__CLASS__.' must be an array that contains database access settings.');
        }

        if (false === isset($params['dbhost']))
        {
            throw new InvalidArgumentException('Class '.__CLASS__.' requires you to set up index "dbhost" in params array.');
        }

        if (false === isset($params['db']))
        {
            throw new InvalidArgumentException('Class '.__CLASS__.' requires you to set up index "db" in params array.');
        }

        if (false === isset($params['dbuser']))
        {
            throw new InvalidArgumentException('Class '.__CLASS__.' requires you to set up index "dbuser" in params array.');
        }

        if (false === isset($params['dbpassword']))
        {
            throw new InvalidArgumentException('Class '.__CLASS__.' requires you to set up index "dbpassword" in params array.');
        }

        $this->_dbhost = $params['dbhost'];

        // @see http://blogs.sun.com/mandalika/entry/demonstrating_the_features_of_mysql
        if (true === $persistent)
        {
            $this->_dbhost = 'p:'.$this->_dbhost;
        }

        $this->_db         = $params['db'];
        $this->_dbuser     = $params['dbuser'];
        $this->_dbpassword = $params['dbpassword'];

        // Optional
        if (true === isset($params['dbport']))
        {
            $this->_dbport = $params['dbport'];
        }

        // Optional
        if (true === isset($params['dbsocket']))
        {
            $this->_dbsocket = $params['dbsocket'];
        }

        // Optional
        if (true === isset($params['dbflag']))
        {
            $this->_dbflag = (int) $params['dbflag'];
        }
    }

    /**
     * Initializes Drizzle and making a resource for use with drizzle_create().
     *
     * @see SpicaDrizzleConnection::_establishConnection()
     */
    protected function _prepareConnection()
    {
        // Create a Drizzle object
        $this->_drizzle = drizzle_create();
        $this->_connectionInitialized = true;
    }

    /**
     * Makes a connection to a MySQL server.
     *
     * @throws SpicaDatabaseException when connection can not establish or the given database/schema is unable to access
     * @see    SpicaDrizzleConnection::_prepareConnection()
     */
    protected function _establishConnection()
    {
        if (false === $this->_connectionInitialized)
        {
            $this->_prepareConnection();
        }

        if (false === $this->_connectionEstablished)
        {
            // Establish a connection to a Drizzle database engine
            if (null !== $this->_dbsocket)
            {
                $conn = drizzle_con_add_uds($this->_drizzle, $this->_dbsocket, $this->_dbuser, $this->_dbpassword, $this->_db, DRIZZLE_CON_NONE);
            }
            else
            {                
                $conn = drizzle_con_add_tcp($this->_drizzle, $this->_dbhost, $this->_dbport, $this->_dbuser, $this->_dbpassword, $this->_db, DRIZZLE_CON_NONE);
            }

            // check connection
            if (false === $conn)
            {
                $errorCode = drizzle_error_code($this->_drizzle);

                if (2002   === $errorCode)
                {
                    throw new SpicaDatabaseConnectionTimeoutException('Unable to connect to database server before connection timeout occurs', drizzle_error($this->_drizzle), null, $errorCode);
                }

                $message = $this->_getMessageByCode($errorCode);
                throw new SpicaDatabaseException($message, drizzle_error($this->_drizzle), null, $errorCode);
            }

            $this->_connection = $conn;
            $this->_isClosed   = false;
            $this->_connectionEstablished = true;
        }
    }

    /**
     * Escapes special characters in a string for use in a SQL statement
     *
     * This method prepends backslashes to the following characters: \x00, \n, \r, \, ', " and \x1a
     *
     * @param  string $variable
     * @return string
     */
    public function quote($variable)
    {
        if (false === $this->_connectionEstablished)
        {
            $this->_establishConnection();
        }

        return drizzle_escape_string($this->_connection, $variable);
    }
}

?>